use std::collections::HashMap;

use serde::Serialize;
use tiled::PropertyValue;

use super::transformation::point_posconv;

#[derive(Debug, Clone, Serialize)]
pub struct TmxVisualData {
    layers: Vec<LayerInfo>,
    properties: HashMap<String, PropertyValueInfo>,
    background_color: Option<(u8, u8, u8, u8)>,
    orientation: self::Orientation,
}

#[derive(Debug, Clone, Serialize)]
pub enum Orientation {
    Orthogonal,
    Isometric,
    Staggered,
    Hexagonal,
}

impl From<tiled::Orientation> for Orientation {
    fn from(orientation: tiled::Orientation) -> Self {
        match orientation {
            tiled::Orientation::Orthogonal => Self::Orthogonal,
            tiled::Orientation::Isometric => Self::Isometric,
            tiled::Orientation::Staggered => Self::Staggered,
            tiled::Orientation::Hexagonal => Self::Hexagonal,
        }
    }
}

trait ExportableObject {
    fn visible(&self) -> bool;
    fn name(&self) -> &str;
    fn properties(&self) -> &tiled::Properties;
}

impl ExportableObject for tiled::Layer<'_> {
    fn visible(&self) -> bool {
        self.visible
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn properties(&self) -> &tiled::Properties {
        &self.properties
    }
}

impl ExportableObject for tiled::Object<'_> {
    fn visible(&self) -> bool {
        self.visible
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn properties(&self) -> &tiled::Properties {
        &self.properties
    }
}

fn check_export(layer: &dyn ExportableObject) -> bool {
    //首先检查是否有配置is_show属性
    let export = match layer.properties().get("export") {
        Some(PropertyValue::BoolValue(boo)) => {
            Some(*boo)
        }
        Some(PropertyValue::IntValue(int)) => {
            Some(*int != 0)
        }

        Some(PropertyValue::FloatValue(float)) => {
            Some(*float != 0.0)
        }

        Some(PropertyValue::StringValue(string)) => {
            Some(string != "false")
        }
        
        _ => {
            None
        }
    };
    if let Some(export) = export {
        return export;
    }

    //检查名字是否以"_"开头
    if layer.name().starts_with("_") {
        return false;
    }

    return layer.visible();
    
}

#[derive(Debug, Clone, Serialize)]
pub struct LayerInfo {
    nodes: Vec<NodeInfo>,
    properties: HashMap<String, PropertyValueInfo>,
    name: String,
    id: u32,
    visible: bool,
}

impl LayerInfo {
    pub fn new() -> Self {
        Self {
            nodes: Vec::new(),
            properties: HashMap::new(),
            name: String::new(),
            visible: true,
            id: 0,
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct NodeInfo {
    position: (f32, f32),
    rotation: f32,
    flip_h: bool,
    flip_v: bool,
    image:Option<String>,
    name: String,
    id: u32,
    content_size: Option<(f32, f32)>,
    visible: bool,
    order: u32,
    properties: HashMap<String, PropertyValueInfo>,
}

impl NodeInfo {
    pub fn new() -> Self {
        Self {
            position: (0.0, 0.0),
            rotation: 0.0,
            flip_h: false,
            flip_v: false,
            image: None,
            name: String::new(),
            id: 0,
            content_size: None,
            properties: HashMap::new(),
            visible: true,
            order: 0,
        }
    }
}

#[derive(Debug, Clone)]
pub enum PropertyValueInfo {
    Bool(bool),
    Int(i32),
    Float(f32),
    String(String),
    Unknown,
}

impl Serialize for PropertyValueInfo {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match self {
            Self::Bool(boo) => {
                serializer.serialize_bool(*boo)
            },
            Self::Int(int) => {
                serializer.serialize_i32(*int)
            },
            Self::Float(float) => {
                serializer.serialize_f32(*float)
            },
            Self::String(string) => {
                serializer.serialize_str(string)
            },
            Self::Unknown => {
                serializer.serialize_none()
            }
        }
    }
}

impl From<&PropertyValue> for PropertyValueInfo {
    fn from(value: &PropertyValue) -> Self {
        match value {
            PropertyValue::BoolValue(boo) => {
                Self::Bool(*boo)
            },
            PropertyValue::IntValue(int) => {
                Self::Int(*int)
            },
            PropertyValue::FloatValue(float) => {
                Self::Float(*float)
            },
            PropertyValue::StringValue(string) => {
                Self::String(string.clone())
            },
            _ => {
                println!("unsupported property value type: {:?}", value);
                Self::Unknown
            }
        }
    }
}

impl From<&super::MapData> for TmxVisualData {
    fn from(map_data: &super::MapData) -> Self {
        let map = &map_data.map;
        let mut layers = Vec::new();
        let mut properties = HashMap::new();

        let base_path = &map_data.work_dir;
        for (key, value) in map.properties.iter() {
            properties.insert(key.clone(), PropertyValueInfo::from(value));
        }

        let background_color = match map.background_color {
            Some(color) => {
                Some((color.red, color.green, color.blue, color.alpha))
            },
            None => {
                None
            }
        };


        for layer in map.layers() {
            if check_export(&layer) == false {
                continue;
            }
            let mut layer_data = LayerInfo::new();
            layer_data.name = layer.name().to_string();
            layer_data.visible = layer.visible();
            layer_data.id = layer.id();
            let properties = layer.properties();
            for (key, value) in properties.iter() {
                layer_data.properties.insert(key.clone(), PropertyValueInfo::from(value));
            }

            match layer.layer_type() {
                tiled::LayerType::Objects(object_layer) => {
                    let mut order_data = match &object_layer.draworder {
                        Some(order)=>{
                            match order.as_str() {
                                "index" => LayerOrder::new_custom(),
                                _ => LayerOrder::new_yorder(),
                            }
                        }
                        None => LayerOrder::new_yorder(),
                    };

                    for object in object_layer.objects().into_iter() {
                        if check_export(&object) == false {
                            continue;
                        }

                        let Some(tile) = object.get_tile() else {
                            continue;
                        };

                        let Some(tile_info) = tile.get_tile() else {
                            continue;
                        };
                        
                        let Some(tile_data) = object.tile_data() else {
                            continue;
                        };

                        let mut node = NodeInfo::new();

                        //将瓦块的属性合并到节点属性中
                        for (key, value) in tile_info.properties.iter() {
                            node.properties.insert(key.clone(), PropertyValueInfo::from(value));
                        }

                        //将对象的属性合并到节点属性中，会覆盖瓦块属性
                        for (key, value) in object.properties().iter() {
                            node.properties.insert(key.clone(), PropertyValueInfo::from(value));
                        }
                        node.position = point_posconv(Some(map), object.x, object.y);
                        node.position.0 += layer.offset_x;
                        node.position.1 -= layer.offset_y;
                        node.rotation = object.rotation;
                        node.flip_h = tile_data.flip_h;
                        node.flip_v = tile_data.flip_v;
                        node.visible = object.visible;
                        node.id = object.id();
                        node.name = object.name().to_string();
                        if let Some(image) = &tile_info.image {
                            let diff_path = pathdiff::diff_paths(&image.source.canonicalize().unwrap(), base_path).unwrap();
                            //去除后缀名
                            let diff_path = diff_path.with_extension("");
                            //保证分隔符为"/"
                            let diff_path = diff_path.to_str().unwrap().replace("\\", "/");
                            node.image = Some(diff_path);
                        }
                        node.content_size = match object.shape {
                            tiled::ObjectShape::Rect { width, height } => {
                                Some((width, height))
                            },
                            _ =>{
                                None
                            }
                        };

                        order_data.push(node);
                    }

                    let mut order_data = order_data.into_vec();
                    for (index, node) in order_data.iter_mut().enumerate() {
                        node.order = index as u32;
                    }

                    println!("order_data: {}", order_data.len());
                    layer_data.nodes.extend(order_data);
                },
                _ => {
                    println!("only support object layer, skip layer: {}", layer.name());
                }
            }

            layers.push(layer_data);
        }



        Self {
            layers,
            properties,
            background_color,
            orientation: map.orientation.into(),
        }
    }
}

enum LayerOrder {
    Custom(Vec<NodeInfo>),
    YOrder(Vec<(f32, NodeInfo)>),
}

impl LayerOrder {
    fn new_custom() -> Self {
        Self::Custom(Vec::new())
    }

    fn new_yorder() -> Self {
        Self::YOrder(Vec::new())
    }
}

impl LayerOrder {
    fn push(&mut self, node:NodeInfo) {
        match self {
            Self::Custom(vec) => {
                vec.push(node);
            },
            Self::YOrder(map) => {
                map.push((node.position.1, node));
                map.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
            }
        }
    }


    #[allow(dead_code)]
    fn len(&self) -> usize {
        match self {
            Self::Custom(vec) => {
                vec.len()
            },
            Self::YOrder(map) => {
                map.len()
            }
        }
    }

    fn into_vec(self) -> Vec<NodeInfo> {
        match self {
            Self::Custom(vec) => {
                vec
            },
            Self::YOrder(map) => {
                map.into_iter().map(|(_, node)| node).collect()
            }
        }
    }
}
